// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file is full of ugly hacks. Reading the code is not recommended. Sincerely.

package main

import (
	"container/list"
	"fmt"
	"github.com/cznic/strutil"
	"go/scanner"
	ast "godeit/go/parser"
	"os"
	"sort"
	"strings"
)

type symtyp int

const (
	_ symtyp = iota
	aCONST
	aVAR
	aFUNC
	aMETHOD
	aTYPE
	aPARAM
	aFIELD
	aEMBED
	aXTYPE
	aXFUNC
	aXMETHOD
)

var symtypes = map[symtyp]string{
	aCONST:   "CONST",
	aVAR:     "VAR",
	aFUNC:    "FUNC",
	aMETHOD:  "METHOD",
	aTYPE:    "TYPE",
	aPARAM:   "PARAM",
	aFIELD:   "FIELD",
	aEMBED:   "EMBED",
	aXTYPE:   "XTYPE",
	aXFUNC:   "XFUNC",
	aXMETHOD: "XMETHOD",
}

func (s symtyp) String() (t string) {
	t = symtypes[s]
	if t == "" {
		t = fmt.Sprintf("<unknown symtype %d>", s)
	}
	return
}

type ctxnfo struct {
	sym *ast.Sym
	typ symtyp
	val interface{}
}

func (c *ctxnfo) String() string {
	return fmt.Sprintf("%v %v %v %#v", c.sym, c.sym.Pos.Position(), c.typ, c.val)
}

type ctx struct {
	parent *ctx
	names  map[string]*ctxnfo
}

func (c *ctx) new() *ctx {
	return &ctx{c, make(map[string]*ctxnfo)}
}

func (c *ctx) insert(ident []byte, nfo *ctxnfo) (alt *ctxnfo) {
	name := string(ident)
	if name == "_" || strings.HasPrefix(name, "_.") {
		return
	}

	var ok bool
	alt, ok = c.names[name]
	if !ok {
		c.names[name] = nfo
	}
	return
}

func (c *ctx) lookup(sym []byte) *ctxnfo {
	return c.names[string(sym)]
}

func (c *ctx) find(sym []byte) (nfo *ctxnfo) {
	name := string(sym)
	for ; c != nil; c = c.parent {
		if nfo = c.names[name]; nfo != nil {
			return
		}
	}
	return
}

func (c *ctx) dump(f strutil.Formatter) {
	if p := c.parent; p != nil {
		p.dump(f)
		f.Format("%i")
	}
	names := []string{}
	for name := range c.names {
		names = append(names, name)
	}
	sort.Strings(names)
	for _, name := range names {
		f.Format("%v\n", c.names[name])
	}
}

func fnrcvr(odclfunc *ast.ODCLFUNC) (rx []byte) {
	if _, ok := odclfunc.Body.(ast.OXGO); ok { // postpone till expansion
		return
	}

	typ := odclfunc.Name.Type.(ast.OTFUNC)
	var rxnode interface{}
	if left := typ.Left; left != nil {
		switch x := left.(type) {
		case ast.OKEY:
			rxnode = x.Right
		case *ast.Sym:
			return x.Name
		case ast.OIND:
			rxnode = x.Type
		default:
			panic(fmt.Errorf("internal error (%s) %T %#v", me(), x, x))
		}
		switch x := rxnode.(type) {
		case ast.OIND:
			rxnode = x.Type
		case *ast.Sym:
			return x.Name
		default:
			panic(fmt.Errorf("internal error (%s) %T %#v", me(), x, x))
		}
		switch x := rxnode.(type) {
		case *ast.Sym:
			return x.Name
		default:
			panic(fmt.Errorf("internal error (%s) %T %#v", me(), x, x))
		}
	}
	return
}

func fnname(odclfunc *ast.ODCLFUNC) (name string) {
	if b := fnrcvr(odclfunc); len(b) != 0 {
		return string(b) + "." + string(odclfunc.Name.Name)
	}

	return string(odclfunc.Name.Name)
}

func fnsym(odclfunc *ast.ODCLFUNC) (sym *ast.Sym) {
	if b := fnrcvr(odclfunc); len(b) != 0 {
		sym = &ast.Sym{}
		*sym = *odclfunc.Name
		sym.Name = []byte(fnname(odclfunc))
		return
	}

	return odclfunc.Name
}

func listwalk(l *list.List, fn func(interface{})) {
	if l == nil {
		return
	}

	for node := l.Front(); node != nil; node = node.Next() {
		fn(node.Value)
	}
}

type argpart struct {
	names []*ast.Sym
	typ   interface{}
}

func args(node interface{}) (s []*argpart) {
	switch x := node.(type) {
	default:
		panic(fmt.Errorf("internal error (%s) %T %#v", me(), x, x))
	case nil:
		return
	case ast.OIND:
		return []*argpart{&argpart{typ: x.Type}}
	case *ast.Sym:
		return []*argpart{&argpart{typ: x}}
	case ast.OKEY:
		return append(s, &argpart{
			[]*ast.Sym{x.Left.(ast.ONONAME).Sym},
			x.Right,
		})
	case *list.List:
		if x == nil || x.Len() == 0 {
			return
		}

		node := x.Back()
		if _, ok := node.Value.(ast.OKEY); !ok { // only type list
			listwalk(x, func(n interface{}) { s = append(s, &argpart{typ: n}) })
			return
		}

		for {
			okey := node.Value.(ast.OKEY)
			part := &argpart{typ: okey.Right}
			part.names = append(part.names, okey.Left.(ast.ONONAME).Sym)
			s = append(s, part)
			for {
				node = node.Prev()
				if node == nil {
					for i, part := range s {
						for j, k := 0, len(part.names)-1; j < k; j, k = j+1, k-1 {
							part.names[j], part.names[k] = part.names[k], part.names[j]
						}
						s[i] = part
					}
					for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
						s[i], s[j] = s[j], s[i]
					}
					return
				}

				if _, ok := node.Value.(ast.OKEY); ok {
					break
				}

				part.names = append(part.names, node.Value.(*ast.Sym))
			}
		}
	}
	panic("unreachable")
}

func rewrite(xtop interface{}) (err error) {
	if !*optDbg {
		defer func() {
			if e := recover(); e != nil {
				err = fmt.Errorf("%v", e)
			}
		}()
	}

	if xtop == nil {
		return
	}

	top := xtop.(*list.List)
	if top == nil {
		return
	}

	var topc *ctx
	c := topc.new()
	errv := &scanner.ErrorVector{}

	insert := func(c *ctx, nfo *ctxnfo) {
		if c == topc || nfo.sym == nil {
			return
		}

		if alt := c.insert(nfo.sym.Name, nfo); alt != nil {
			errv.Error(nfo.sym.Pos.Position(), fmt.Sprintf(
				"%s redeclared, previous at %s",
				nfo.sym.Name, alt.sym.Pos.Position(),
			))
		}
	}

	listwalk(top, func(v interface{}) {
		switch x := v.(type) {
		case ast.OCONST:
			if x.Names != nil {
				listwalk(x.Names, func(n interface{}) { insert(c, &ctxnfo{sym: n.(*ast.Sym), typ: aCONST}) })
			}
		case ast.OVAR:
			if x.Names != nil {
				listwalk(x.Names, func(n interface{}) { insert(c, &ctxnfo{sym: n.(*ast.Sym), typ: aVAR}) })
			}
		case ast.ODCLFUNC:
			if string(x.Name.Name) == "_" {
				break
			}

			if fnrcvr := fnrcvr(&x); len(fnrcvr) != 0 {
				insert(c, &ctxnfo{sym: fnsym(&x), typ: aMETHOD})
				break
			}

			if string(x.Name.Name) != "init" {
				insert(c, &ctxnfo{sym: x.Name, typ: aFUNC})
				break
			}

			if c.lookup([]byte("init")) == nil {
				insert(c, &ctxnfo{sym: x.Name, typ: aFUNC})
			}
		case ast.ODCLTYPE:
			if sym := x.Sym; sym != nil {
				insert(c, &ctxnfo{sym: sym, typ: aTYPE})
			}
		case ast.OXGO:
			switch body := x.Body; xx := body.(type) {
			default:
				panic(fmt.Errorf("internal error (%s) %T %#v", me(), xx, xx))
			case ast.ODCLTYPE:
				insert(c, &ctxnfo{sym: xx.Sym, typ: aXTYPE, val: v})
			case ast.ODCLFUNC:
				if string(xx.Name.Name) == "_" {
					break
				}

				name := fnname(&xx)
				if name == "init" {
					errv.Error(xx.Name.Pos.Position(), "invalid name 'init'")
					break
				}

				if fnrcvr := fnrcvr(&xx); len(fnrcvr) != 0 {
					insert(c, &ctxnfo{sym: fnsym(&xx), typ: aXMETHOD, val: xx})
					break
				}

				insert(c, &ctxnfo{sym: xx.Name, typ: aXFUNC, val: xx})
			}
		default:
			panic(fmt.Errorf("internal error (%s) %T %#v", me(), x, x))
		}
	})

	var walk func(interface{}, *ctx, interface{}, bool)

	argwalk := func(c *ctx, node interface{}, clone bool) {
		for _, part := range args(node) {
			for _, name := range part.names {
				insert(c, &ctxnfo{sym: name, typ: aPARAM})
			}
			walk(nil, c, &part.typ, clone)
		}
	}

	walk = func(xc interface{}, c *ctx, node interface{}, clone bool) {
		ip, isip := node.(*interface{})
		if isip {
			node = *ip
		}
		switch x := node.(type) {
		default:
			c.dump(strutil.IndentFormatter(os.Stdout, ". ")) //TODO-
			panic(fmt.Errorf("internal error (%s) %T %#v", me(), x, x))
		case nil:
			// nop
		case *list.List: // via ptr to an inteface{}
			if x == nil {
				break
			}

			switch clone {
			case false:
				listwalk(x, func(n interface{}) {
					walk(nil, c, &n, false)
				})
			case true:
				nx := list.New()
				listwalk(x, func(n interface{}) {
					walk(nil, c, &n, true)
					nx.PushBack(n)
				})
				x = nx
			}
			for node := x.Front(); node != nil; {
				next := node.Next()
				if y, ok := node.Value.(ast.OXGO); ok {
					if y.IsDeclaration() { //TODO+ adjust ODCLTYPE SrcJoin
						next = node.Next()
						x.Remove(node)
					}
				}
				node = next
			}
			if clone {
				*ip = x
			}
		case **list.List:
			if x == nil || *x == nil {
				break
			}

			switch clone {
			case false:
				walk(nil, c, *x, false)
			case true:
				nx := list.New()
				listwalk(*x, func(n interface{}) {
					walk(nil, c, &n, true)
					nx.PushBack(n)
				})
				*x = nx
			}
			for node := x.Front(); node != nil; {
				next := node.Next()
				if y, ok := node.Value.(ast.OXGO); ok {
					if y.IsDeclaration() { //TODO+ adjust ODCLTYPE SrcJoin
						next = node.Next()
						x.Remove(node)
					}
				}
				node = next
			}
		case ast.OADD:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Left, clone)
			walk(nil, c, &x.Right, clone)
		case ast.OADDR:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Left, clone)
		case ast.OAND:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Left, clone)
			walk(nil, c, &x.Right, clone)
		case ast.OANDAND:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Left, clone)
			walk(nil, c, &x.Right, clone)
		case ast.OANDNOT:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Left, clone)
			walk(nil, c, &x.Right, clone)
		case ast.OAS:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Left, clone)
			walk(nil, c, &x.Right, clone)
		case ast.OAS2:
			if clone {
				*ip = x
			}
			if x.Colas {
				listwalk(x.List, func(n interface{}) {
					if sym := n.(*ast.Sym); c.lookup(sym.Name) == nil {
						insert(c, &ctxnfo{sym: sym, typ: aVAR})
					}
				})
			}
			walk(nil, c, &x.Rlist, clone)
		case ast.OASOP:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Left, clone)
			walk(nil, c, &x.Right, clone)
		case ast.OBLOCK:
			if clone {
				*ip = x
			}
			nc := c.new()
			walk(nil, nc, &x.List, clone)
		case ast.OBREAK:
			if clone {
				*ip = x
			}
			// nop
		case ast.OCALL:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Left, clone)
			walk(nil, c, &x.List, clone)
		case ast.OCLOSURE:
			if clone {
				*ip = x
			}
			nc := c.new()
			walk(nil, nc, &x.Type, clone)
			walk(nil, nc, &x.Body, clone)
		case ast.OCOM:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Left, clone)
		case ast.OCOMPLIT:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Right, clone)
			walk(nil, c, &x.List, clone)
		case ast.OCONST:
			if clone {
				*ip = x
			}
			if x.Names == nil {
				break
			}

			listwalk(x.Names, func(n interface{}) { insert(c, &ctxnfo{sym: n.(*ast.Sym), typ: aCONST}) })
			walk(nil, c, &x.Type, clone)
			walk(nil, c, &x.Expr, clone)
		case ast.OCONTINUE:
			if clone {
				*ip = x
			}
			// nop
		case ast.ODCLFIELD:
			if clone {
				*ip = x
			}
			// func/method args handled elsewhere, here we go with structs and interfaces only
			if l := x.Left; l != nil { // struct
				insert(c, &ctxnfo{sym: l.(*ast.Sym), typ: aFIELD})
			}
			walk(nil, c, &x.Right, clone)
		case ast.ODCLFUNC:
			if clone {
				*ip = x
			}
			nc, typ := c.new(), x.Name.Type.(ast.OTFUNC)
			argwalk(nc, typ.Left, clone)
			argwalk(nc, typ.List, clone)
			argwalk(nc, typ.Rlist, clone)
			walk(x, nc, &x.Body, clone)
		case ast.ODCLTYPE:
			if clone {
				*ip = x
			}
			sym := x.Sym
			if sym == nil {
				break
			}

			insert(c, &ctxnfo{sym: sym, typ: aTYPE})
			walk(x, c, &sym.Type, clone)
		case ast.ODDD:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Left, clone)
		case ast.ODEFER:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Left, clone)
		case ast.ODIV:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Left, clone)
			walk(nil, c, &x.Right, clone)
		case ast.ODOTTYPE:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Left, clone)
			walk(nil, c, &x.Right, clone)
		case ast.OEMBEDED:
			if clone {
				*ip = x
			}
			insert(c, &ctxnfo{sym: x.Sym, typ: aEMBED}) //TODO- packname
			walk(nil, c, &x.Sym, clone)
		case ast.OEMPTY:
			if clone {
				*ip = x
			}
			// nop
		case ast.OEQ:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Left, clone)
			walk(nil, c, &x.Right, clone)
		case ast.OFOR:
			if clone {
				*ip = x
			}
			nc := c.new()
			walk(nil, nc, &x.Init, clone)
			walk(nil, nc, &x.Test, clone)
			walk(nil, nc, &x.Incr, clone)
			walk(nil, nc, &x.Body, clone)
		case ast.OGE:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Left, clone)
			walk(nil, c, &x.Right, clone)
		case ast.OGOTO:
			if clone {
				*ip = x
			}
			// nop
		case ast.OGT:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Left, clone)
			walk(nil, c, &x.Right, clone)
		case ast.OIF:
			if clone {
				*ip = x
			}
			nc := c.new()
			walk(nil, nc, &x.Init, clone)
			walk(nil, nc, &x.Test, clone)
			walk(nil, nc, &x.Body, clone)
			walk(nil, nc, &x.Else, clone)
		case ast.OIND:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Type, clone)
		case ast.OINDEX:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Left, clone)
			walk(nil, c, &x.Right, clone)
		case ast.OKEY:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Left, clone)
			walk(nil, c, &x.Right, clone)
		case ast.OLABEL:
			if clone {
				*ip = x
			}
			// nop
		case ast.OLE:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Left, clone)
			walk(nil, c, &x.Right, clone)
		case ast.OLT:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Left, clone)
			walk(nil, c, &x.Right, clone)
		case ast.OLSH:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Left, clone)
			walk(nil, c, &x.Right, clone)
		case ast.OMINUS:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Left, clone)
		case ast.OMOD:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Left, clone)
			walk(nil, c, &x.Right, clone)
		case ast.OMUL:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Left, clone)
			walk(nil, c, &x.Right, clone)
		case ast.ONE:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Left, clone)
			walk(nil, c, &x.Right, clone)
		case ast.ONOT:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Left, clone)
		case ast.OOR:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Left, clone)
			walk(nil, c, &x.Right, clone)
		case ast.OOROR:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Left, clone)
			walk(nil, c, &x.Right, clone)
		case ast.OPAREN:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Left, clone)
		case ast.OPLUS:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Left, clone)
		case ast.OPROC:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Left, clone)
		case ast.ORANGE:
			if clone {
				*ip = x
			}
			nc := c.new()
			if x.Colas {
				listwalk(x.List, func(n interface{}) { insert(nc, &ctxnfo{sym: n.(*ast.Sym), typ: aVAR}) })
			}
			walk(nil, c, &x.Right, clone)
			walk(nil, nc, &x.Body, clone)
		case ast.ORETURN:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.List, clone)
		case ast.ORECV:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Left, clone)
		case ast.ORSH:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Left, clone)
			walk(nil, c, &x.Right, clone)
		case ast.OSELECT:
			if clone {
				*ip = x
			}
			nc := c.new()
			walk(nil, nc, &x.List, clone)
		case ast.OSEND:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Left, clone)
			walk(nil, c, &x.Right, clone)
		case ast.OSLICE:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Left, clone)
			walk(nil, c, &x.Right, clone)
		case ast.OSUB:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Left, clone)
			walk(nil, c, &x.Right, clone)
		case ast.OSWITCH:
			if clone {
				*ip = x
			}
			nc := c.new()
			walk(nil, nc, &x.OIF.Init, clone)
			walk(nil, nc, &x.OIF.Test, clone)
			if x.OIF.Body != nil || x.OIF.Else != nil {
				panic(fmt.Errorf("internal error (%s) %T %#v", me(), x, x))
			}
			walk(nil, nc, &x.List, clone)
		case ast.OTARRAY:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Left, clone)
			walk(nil, c, &x.Right, clone)
		case ast.OTCHAN:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Left, clone)
		case ast.OTFUNC:
			if clone {
				*ip = x
			}
			nc := c.new()
			argwalk(nc, x.Left, clone)
			argwalk(nc, x.List, clone)
			argwalk(nc, x.Rlist, clone)
		case ast.OTINTER:
			if clone {
				*ip = x
			}
			nc := c.new()
			walk(nil, nc, &x.List, clone)
		case ast.OTMAP:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Left, clone)
			walk(nil, c, &x.Right, clone)
		case ast.OTPAREN:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Left, clone)
		case ast.OTSTRUCT:
			if clone {
				*ip = x
			}
			nc := c.new()
			walk(nil, nc, &x.List, clone)
		case ast.OTYPESW:
			if clone {
				*ip = x
			}
			nc := c.new()
			switch lft := x.Left.(type) {
			default:
				//fmt.Println(x.Right.(*ast.Sym).Pos.Position())
				//nc.dump(strutil.IndentFormatter(os.Stdout, ". ")) //TODO-
				panic(fmt.Errorf("internal error (%s) %T %#v", me(), lft, lft))
			case *ast.Sym:
				insert(nc, &ctxnfo{sym: lft, typ: aVAR})
			case nil:
				// nop
			}
			walk(nil, nc, &x.Right, clone)
		case ast.OVAR:
			if clone {
				*ip = x
			}
			if x.Names == nil {
				break
			}

			listwalk(x.Names, func(n interface{}) { insert(c, &ctxnfo{sym: n.(*ast.Sym), typ: aVAR}) })
			walk(nil, c, &x.Type, clone)
			walk(nil, c, &x.Expr, clone)
		case ast.OXCASE:
			if clone {
				*ip = x
			}
			nc := c.new()
			walk(nil, nc, &x.List, clone)
			walk(nil, nc, &x.Body, clone)
		case ast.OXDOT:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Left, clone)
			walk(nil, c, &x.Sym, clone)
		case ast.OXFALL:
			if clone {
				*ip = x
			}
			// nop
		case ast.OXGO:
			if clone {
				*ip = x
			}
			if x.IsDeclaration() {
				names := map[string]bool{}
				for node := x.Args.Front(); node != nil; node = node.Next() {
					sym := node.Value.(*ast.Sym)
					name := string(sym.Name)
					if names[name] {
						errv.Error(sym.Pos.Position(), fmt.Sprintf("%s redeclared", name))
					}
					names[name] = true
				}
				return
			}

			var sym *ast.Sym
			switch body := x.Body.(type) {
			default:
				panic(fmt.Errorf("internal error (%s) %T %#v", me(), body, body))
			case ast.OXDOT:
				errv.Error(body.Left.(*ast.Sym).Pos.Position(), "package qualifier unsupported (yet)")
				return
			case *ast.Sym:
				sym = body
				if sym.Pkg != nil {
					errv.Error(sym.Pos.Position(), "package qualifier unsupported (yet)")
					return
				}

			}

			xname := sym.Name
			xnfo := c.find(xname)
			if xnfo == nil {
				errv.Error(sym.Pos.Position(), fmt.Sprintf("%q undefined", xname))
				return
			}

			xtyp := xnfo.typ
			switch xtyp {
			case aCONST, aVAR, aFUNC, aMETHOD, aTYPE, aPARAM, aFIELD, aEMBED:
				errv.Error(sym.Pos.Position(), fmt.Sprintf(
					"%q (%s) is not a xgo declaration (%s)",
					xname, xnfo.sym.Pos.Position(), xtyp,
				))
				return
			}
			//panic(fmt.Errorf("TODO xgo.use (%s) %T %#v", me(), x, x))
		case ast.OXOR:
			if clone {
				*ip = x
			}
			walk(nil, c, &x.Left, clone)
			walk(nil, c, &x.Right, clone)
		case ast.FLOAT:
			if clone {
				*ip = x
			}
			// nop
		case ast.IMAG:
			if clone {
				*ip = x
			}
			// nop
		case ast.INT:
			if clone {
				*ip = x
			}
			// nop
		case ast.STR:
			if clone {
				*ip = x
			}
			// nop
		case *ast.Sym:
			if clone {
				nx := *x
				x = &nx
				*ip = x
			}
			walk(nil, c, &x.Type, clone)
		case **ast.Sym:
			nx := **x
			if clone {
				*x = &nx
			}
			walk(nil, c, &nx.Type, clone)
		}
	}

	topc = c
	//walk(nil, c, &xtop, true) //TODO-
	walk(nil, c, &xtop, false)

	if errors := errv.GetErrorList(scanner.Sorted); len(errors) != 0 {
		err = errors
	}
	return
}
